#ifndef __MEMORY__H__
#define __MEMORY__H__

#include "libs/types.h"
#include "libs/lib.h"


//	8Bytes per cell
#define PTRS_PER_PAGE	512

/*

*/

#define PAGE_OFFSET	((uint64_t)0xffff800000000000)

#define PAGE_GDT_SHIFT	39
#define PAGE_1G_SHIFT	30
#define PAGE_2M_SHIFT	21
#define PAGE_4K_SHIFT	12

#define PAGE_2M_SIZE	(1UL << PAGE_2M_SHIFT)
#define PAGE_4K_SIZE	(1UL << PAGE_4K_SHIFT)

#define PAGE_2M_MASK	(~ (PAGE_2M_SIZE - 1))
#define PAGE_4K_MASK	(~ (PAGE_4K_SIZE - 1))

#define PAGE_2M_ALIGN(addr)	(((uint64_t)(addr) + PAGE_2M_SIZE - 1) & PAGE_2M_MASK)
#define PAGE_4K_ALIGN(addr)	(((uint64_t)(addr) + PAGE_4K_SIZE - 1) & PAGE_4K_MASK)

#define Virt_To_Phy(addr)	((uint64_t)(addr) - PAGE_OFFSET)
#define Phy_To_Virt(addr)	((uint64_t *)((uint64_t)(addr) + PAGE_OFFSET))

#define Virt_To_2M_Page(kaddr)	(MEMORY->globalMemoryDesc.pages_struct + (Virt_To_Phy(kaddr) >> PAGE_2M_SHIFT))
#define Phy_to_2M_Page(kaddr)	(MEMORY->globalMemoryDesc.pages_struct + ((uint64_t)(kaddr) >> PAGE_2M_SHIFT))



////page table attribute

//	bit 63	Execution Disable:
#define PAGE_XD		(uint64_t)0x1000000000000000

//	bit 12	Page Attribute Table
#define	PAGE_PAT	(uint64_t)0x1000

//	bit 8	Global Page:1,global;0,part
#define	PAGE_Global	(uint64_t)0x0100

//	bit 7	Page Size:1,big page;0,small page;
#define	PAGE_PS		(uint64_t)0x0080

//	bit 6	Dirty:1,dirty;0,clean;
#define	PAGE_Dirty	(uint64_t)0x0040

//	bit 5	Accessed:1,visited;0,unvisited;
#define	PAGE_Accessed	(uint64_t)0x0020

//	bit 4	Page Level Cache Disable
#define PAGE_PCD	(uint64_t)0x0010

//	bit 3	Page Level Write Through
#define PAGE_PWT	(uint64_t)0x0008

//	bit 2	User Supervisor:1,user and supervisor;0,supervisor;
#define	PAGE_U_S	(uint64_t)0x0004

//	bit 1	Read Write:1,read and write;0,read;
#define	PAGE_R_W	(uint64_t)0x0002

//	bit 0	Present:1,present;0,no present;
#define	PAGE_Present	(uint64_t)0x0001


//1,0
#define PAGE_KERNEL_GDT		(PAGE_R_W | PAGE_Present)

//1,0	
#define PAGE_KERNEL_Dir		(PAGE_R_W | PAGE_Present)

//7,1,0
#define	PAGE_KERNEL_Page	(PAGE_PS  | PAGE_R_W | PAGE_Present)

//1,0
#define PAGE_USER_GDT		(PAGE_U_S | PAGE_R_W | PAGE_Present)


//2,1,0
#define PAGE_USER_Dir		(PAGE_U_S | PAGE_R_W | PAGE_Present)

//7,2,1,0
#define	PAGE_USER_Page		(PAGE_PS  | PAGE_U_S | PAGE_R_W | PAGE_Present)









////alloc_pages zone_select

//
#define ZONE_DMA	(1 << 0)

//
#define ZONE_NORMAL	(1 << 1)

//
#define ZONE_UNMAPED	(1 << 2)

////struct page attribute (alloc_pages flags)

//	mapped=1 or un-mapped=0 
#define PG_PTable_Maped	(1 << 0)

//	init-code=1 or normal-code/data=0
#define PG_Kernel_Init	(1 << 1)

//	device=1 or memory=0
#define PG_Device	(1 << 2)

//	kernel=1 or user=0
#define PG_Kernel	(1 << 3)

//	shared=1 or single-use=0 
#define PG_Shared	(1 << 4)



typedef struct {uint64_t pml4t;} pml4t_t;
#define	mk_mpl4t(addr,attr)	((uint64_t)(addr) | (uint64_t)(attr))
#define set_mpl4t(mpl4tptr,mpl4tval)	(*(mpl4tptr) = (mpl4tval))


typedef struct {uint64_t pdpt;} pdpt_t;
#define mk_pdpt(addr,attr)	((uint64_t)(addr) | (uint64_t)(attr))
#define set_pdpt(pdptptr,pdptval)	(*(pdptptr) = (pdptval))


typedef struct {uint64_t pdt;} pdt_t;
#define mk_pdt(addr,attr)	((uint64_t)(addr) | (uint64_t)(attr))
#define set_pdt(pdtptr,pdtval)		(*(pdtptr) = (pdtval))


typedef struct {uint64_t pt;} pt_t;
#define mk_pt(addr,attr)	((uint64_t)(addr) | (uint64_t)(attr))
#define set_pt(ptptr,ptval)		(*(ptptr) = (ptval))

void mapApicBaseAddress(uint64_t address,int isUser,int isForce);

struct MemoryE820Info
{
    uint64_t address;
    uint64_t length;
    uint32_t type;

}__attribute__((packed));



struct Page;
struct Zone;
struct GlobalMemoryDescriptor;





struct Page
{
	Zone*	zone_struct;
	uint64_t	PHY_address;
	uint64_t	attribute;

	uint64_t	reference_count;
	
	uint64_t	age;
}__attribute__((packed));



struct Zone
{
	Page * 	pages_group;
	uint64_t	pages_length;
	
	uint64_t	zone_start_address;
	uint64_t	zone_end_address;
	uint64_t	zone_length;
	uint64_t	attribute;

	GlobalMemoryDescriptor* GMD_struct;

	uint64_t	page_using_count;
	uint64_t	page_free_count;

	uint64_t	total_pages_link;
}__attribute__((packed));

struct GlobalMemoryDescriptor
{
	MemoryE820Info 	e820[32];
	uint64_t 	e820_length;	

    uint64_t * bits_map;
	uint64_t 	bits_size;
	uint64_t   bits_length;

	Page*	pages_struct;
	uint64_t	pages_size;
	uint64_t 	pages_length;

	Zone * 	zones_struct;
	uint64_t	zones_size;
	uint64_t 	zones_length;

	uint64_t 	start_code , end_code , end_data , end_rodata,start_brk;

	uint64_t	end_of_struct;	
}__attribute__((packed));



#define MAX_NR_ZONES	10	//max zone

#define SIZEOF_LONG_ALIGN(size) ((size + sizeof(long) - 1) & ~(sizeof(long) - 1) )
#define SIZEOF_INT_ALIGN(size) ((size + sizeof(int) - 1) & ~(sizeof(int) - 1) )

/*

*/

#define	flush_tlb_one(addr)	\
	__asm__ __volatile__	("invlpg	(%0)	\n\t"::"r"(addr):"memory")
/*

*/

#define flush_tlb()						\
do								\
{								\
	uint64_t	tmpreg;					\
	__asm__ __volatile__ 	(				\
				"movq	%%cr3,	%0	\n\t"	\
				"movq	%0,	%%cr3	\n\t"	\
				:"=r"(tmpreg)			\
				:				\
				:"memory"			\
				);				\
}while(0)

/*

*/

inline uint64_t * Get_gdt()
{
	uint64_t * tmp;
	__asm__ __volatile__	(
					"movq	%%cr3,	%0	\n\t"
					:"=r"(tmp)
					:
					:"memory"
				);
	return tmp;
}



class slab
{
private:

public:
	List list;
	Page* page;
	uint64_t using_count;
	uint64_t free_count;
	void* Vaddress;

	uint64_t color_length;
	uint64_t color_count;

	uint64_t* color_map;
	slab(/* args */);
	~slab();
};



class slabcache
{
private:
	/* data */

	


public:
	uint64_t size;

	uint64_t total_using;
	uint64_t total_free;

	slab* cache_pool;
	slab* cache_dma_pool;

	void *(*constructor)(void* Vaddress,uint64_t arg);
	void *(*destructor)(void* Vaddress,uint64_t arg);
	slabcache(/* args */);
	~slabcache();
};





class memorymanage
{
private:
	
public:
	static memorymanage* memory;
    GlobalMemoryDescriptor globalMemoryDesc;
    slabcache kmalloc_cache_size[16];
    memorymanage(/* args */);
    ~memorymanage();

	//physical memory 
	void initMemoryManage();
    uint64_t page_init(struct Page * page,uint64_t flags);
    uint64_t page_clean(struct Page * page);
    Page* alloc_pages(int zone_select,int number,uint64_t page_flags);
	void free_pages(Page * page,int number);
	uint64_t set_page_attribute(Page* page,uint64_t flags);
	uint64_t get_page_attribute( Page * page);

	//slab memory pool
	void initSlabCachePool();
	void* kmalloc(uint64_t size, uint64_t gfp_flages);
	slab * kmalloc_create(uint64_t size);
	uint64_t kfree(void* address);

	//page table
	void initPageTable();
	void frame_buffer_init();
	void get_page_table_entry(uint64_t virtualaddress,uint64_t** pml4e,uint64_t** pdpte,uint64_t** pdte);
	void get_page_table_index(uint64_t virtualaddress,uint64_t* pml4e,uint64_t* pdpte,uint64_t* pdte);


	uint64_t doBrk(uint64_t endBrk,uint64_t length);
};


void* operator new(unsigned long size);
void* operator new[](unsigned long size);
void operator delete(void* ptr);
void operator delete[](void* ptr);
void operator delete(void* ptr,unsigned long size);


#define MEMORY \
memorymanage::memory

#endif